package com.example.synatx.lombda;

import com.example.synatx.lesson2.Apple;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;

/**
 * @Author guoxh
 * @Date 2020/2/25 22:11
 * @Desc
 **/
public class LDTest {

    public static String processFile() throws IOException {
        try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
            return br.readLine();
        }
    }

    public static String processFile(BufferrReaderProcessor p) throws IOException{
        try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){
            return p.process(br);
        }
    }

    public void readOneLine() throws IOException {
        String result = processFile((BufferedReader br) -> br.readLine());
    }

    public void readTwoLine() throws IOException {
        String result = processFile( (BufferedReader br)-> br.readLine() + br.readLine() );
    }

    /**
     * Predicate 接受一个泛型对象,并返回一个boolean.
     * @param list
     * @param p
     * @param <T>
     * @return
     */
    public static <T> List<T> filter(List<T> list, Predicate<T> p){
        List<T> results = new ArrayList<>();
        for (T t : list){
            if (p.test(t)){
                results.add(t);
            }
        }
        return results;
    }

    public void predicateTest(){
        Predicate<String> emptyStr = (String s) -> s.isEmpty();
        Predicate<String> noEmptyStr = (String s) -> !s.isEmpty();

        List<String> listOfString = new ArrayList<>();
        List<String> nonEmptyList = filter(listOfString,noEmptyStr);
    }


    /**
     *  Consumer 访问类型为T的对象,并对其执行某些操作.
     * @param list
     * @param c
     * @param <T>
     */
    public static <T> void forEach(List<T> list, Consumer<T> c){
        for(T t: list){
            c.accept(t);
        }
    }

    public void consumerTest(){
        forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
    }

    /**
     * 它接受一个泛型T的对象,并返回一个泛型R的对象.
     * @param list
     * @param f
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T,R> List<R> map(List<T> list, Function<T,R> f){
        List<R> result = new ArrayList<>();
        for(T t : list){
            result.add(f.apply(t));
        }
        return result;
    }

    public void functionTest(){
        List<Integer> result = map(Arrays.asList("demo","user","on"),(String s)-> s.length());
    }


    /**
     * 自动拆装箱 损耗性能
     * 装箱操作是把原始类型包裹起来并放在堆内存中.因此 装箱后的值会占用更大的内存,并需要额外的内存搜索来获取被包裹的原始值
     * IntPredicate 可以避免装箱操作.
     */
    public void checkEven(){
        //无需装箱
        IntPredicate p1 = (int i) -> i%2==0;
        p1.test(1000);

        //装箱
        Predicate<Integer> p2 = (Integer i) -> i%2 != 0;
        p2.test(1000);
    }

    public void test02(){
        Runnable r = ()->{System.out.println("runnable");};
    }

    /**
     *lambda 表达式 类型推断
     * Java 编译器会从上下文(目标类型)推断出用什么函数接口来配合Lambda表达式.
     */
    public void test03(){
        List<Apple> appleList = new ArrayList<>();
        List<Apple> apples = filter(appleList,(Apple a) -> a.getWeight()>150);

        //类型推断
        List<Apple> apples1 = filter(appleList,a -> a.getWeight()>150);
    }

    int portNumber = 8080;

    /**
     * lambda可以随意的使用实例变量和静态变量,但是局部变量必须显式声明final或者实际上就是final.lambda表达式只能捕获指派给它们的局部变量一次.
     * 实例变量和静态变量是存储到堆中,局部变量是存储在栈上.lamdba是在一个线程中使用的
     * 堆的数据是线程之间共享的,而栈内的数据是线程独享的
     */
    public void test04(){
        int number = 9091;
        Runnable r = ()-> System.out.println(portNumber);
        Runnable r2 = () -> System.out.println(number);
        //number = 9092;
    }



}
